<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HBM 动态可视化</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: 'Consolas', 'Monaco', monospace;
            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
            color: #e0e0e0;
            padding: 20px;
            min-height: 100vh;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        h1 {
            text-align: center;
            color: #00d4ff;
            margin-bottom: 10px;
            font-size: 32px;
            text-shadow: 0 0 20px rgba(0, 212, 255, 0.5);
        }
        .subtitle {
            text-align: center;
            color: #888;
            margin-bottom: 30px;
        }
        .controls {
            background: rgba(37, 37, 37, 0.8);
            padding: 20px;
            border-radius: 12px;
            margin-bottom: 20px;
            display: flex;
            gap: 15px;
            align-items: center;
            flex-wrap: wrap;
            border: 1px solid #333;
        }
        button {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 12px 24px;
            border-radius: 8px;
            cursor: pointer;
            font-size: 14px;
            font-weight: bold;
            transition: all 0.3s;
            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
        }
        button:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
        }
        button:active {
            transform: translateY(0);
        }
        button:disabled {
            background: #555;
            cursor: not-allowed;
            box-shadow: none;
        }
        .step-info {
            flex: 1;
            min-width: 200px;
            text-align: center;
            font-size: 16px;
            color: #00d4ff;
            font-weight: bold;
        }
        .speed-control {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        input[type="range"] {
            width: 150px;
        }
        .main-display {
            background: rgba(37, 37, 37, 0.8);
            padding: 30px;
            border-radius: 12px;
            border: 1px solid #333;
        }
        .event-banner {
            padding: 20px;
            border-radius: 8px;
            margin-bottom: 25px;
            font-size: 18px;
            font-weight: bold;
            text-align: center;
            border-left: 5px solid;
            transition: all 0.3s;
        }
        .event-reload {
            background: rgba(0, 255, 136, 0.1);
            border-color: #00ff88;
            color: #00ff88;
        }
        .event-offload {
            background: rgba(255, 107, 53, 0.1);
            border-color: #ff6b35;
            color: #ff6b35;
        }
        .event-visit {
            background: rgba(157, 78, 221, 0.1);
            border-color: #9d4edd;
            color: #9d4edd;
        }
        .event-fin {
            background: rgba(6, 255, 165, 0.1);
            border-color: #06ffa5;
            color: #06ffa5;
        }
        .usage-bar {
            margin-bottom: 30px;
        }
        .usage-label {
            display: flex;
            justify-content: space-between;
            margin-bottom: 10px;
            font-size: 16px;
        }
        .usage-text {
            color: #00d4ff;
            font-weight: bold;
        }
        .progress-container {
            height: 30px;
            background: #2a2a2a;
            border-radius: 15px;
            overflow: hidden;
            position: relative;
            border: 2px solid #444;
        }
        .progress-bar {
            height: 100%;
            background: linear-gradient(90deg, #00ff88, #00d4ff);
            transition: width 0.5s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-weight: bold;
            box-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
        }
        .memory-viz {
            margin-bottom: 30px;
        }
        .memory-label {
            margin-bottom: 15px;
            font-size: 16px;
            color: #00d4ff;
            font-weight: bold;
        }
        .memory-bar {
            height: 40px;
            background: #2a2a2a;
            border-radius: 8px;
            position: relative;
            border: 2px solid #444;
            overflow: hidden;
        }
        .memory-segment {
            position: absolute;
            height: 100%;
            background: linear-gradient(180deg, #00ff88, #00cc70);
            border-right: 2px solid #1a1a1a;
            transition: all 0.5s ease;
        }
        .memory-segment:hover {
            filter: brightness(1.2);
            z-index: 10;
        }
        .regions-list {
            background: #1a1a1a;
            padding: 20px;
            border-radius: 8px;
            border: 1px solid #333;
        }
        .regions-title {
            color: #00d4ff;
            font-weight: bold;
            margin-bottom: 15px;
            font-size: 16px;
        }
        .region-item {
            padding: 10px;
            margin: 8px 0;
            background: rgba(0, 212, 255, 0.05);
            border-left: 3px solid #00d4ff;
            border-radius: 4px;
            font-family: monospace;
            transition: all 0.3s;
        }
        .region-item:hover {
            background: rgba(0, 212, 255, 0.1);
            transform: translateX(5px);
        }
        .tooltip {
            position: absolute;
            background: rgba(0, 0, 0, 0.9);
            color: white;
            padding: 8px 12px;
            border-radius: 6px;
            font-size: 12px;
            pointer-events: none;
            z-index: 1000;
            display: none;
            border: 1px solid #00d4ff;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🧠 HBM 虚拟内存动态可视化</h1>
        <div class="subtitle">逐步查看虚拟地址空间在 HBM 中的加载过程</div>
        
        <div class="controls">
            <button onclick="firstStep()">⏮ 第一步</button>
            <button onclick="prevStep()">◀ 上一步</button>
            <button onclick="playPause()" id="playBtn">▶ 播放</button>
            <button onclick="nextStep()">下一步 ▶</button>
            <button onclick="lastStep()">最后一步 ⏭</button>
            
            <div class="step-info" id="stepInfo">步骤 1/22</div>
            
            <div class="speed-control">
                <label>速度:</label>
                <input type="range" id="speedSlider" min="100" max="2000" value="1000" step="100">
                <span id="speedLabel">1.0x</span>
            </div>
        </div>
        
        <div class="main-display">
            <div class="event-banner" id="eventBanner">
                准备开始...
            </div>
            
            <div class="usage-bar">
                <div class="usage-label">
                    <span class="usage-text">HBM 使用量</span>
                    <span id="usageText">0 / 320 (0.0%)</span>
                </div>
                <div class="progress-container">
                    <div class="progress-bar" id="progressBar" style="width: 0%">0%</div>
                </div>
            </div>
            
            <div class="memory-viz">
                <div class="memory-label">虚拟地址空间 [0 ~ 700]</div>
                <div class="memory-bar" id="memoryBar"></div>
            </div>
            
            <div class="regions-list">
                <div class="regions-title">HBM 中的虚拟内存区域</div>
                <div id="regionsList">
                    <div style="color: #888; text-align: center; padding: 20px;">
                        (空)
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <div class="tooltip" id="tooltip"></div>
    
    <script>
        const states = [{"timestamp": 0, "type": "reload", "desc": "\u52a0\u8f7d\u865a\u62df\u5730\u5740 [0, 120) \u5171 120 \u5b57\u8282", "usage": 120, "regions": [[0, 120]]}, {"timestamp": 4800, "type": "reload", "desc": "\u52a0\u8f7d\u865a\u62df\u5730\u5740 [120, 220) \u5171 100 \u5b57\u8282", "usage": 220, "regions": [[0, 120], [120, 100]]}, {"timestamp": 8800, "type": "visit", "desc": "\u8bbf\u95ee\u8bf7\u6c42 R0", "usage": 220, "regions": [[0, 120], [120, 100]]}, {"timestamp": 8800, "type": "visit", "desc": "\u8bbf\u95ee\u8bf7\u6c42 R1", "usage": 220, "regions": [[0, 120], [120, 100]]}, {"timestamp": 11800, "type": "offload", "desc": "\u5378\u8f7d\u865a\u62df\u5730\u5740 [0, 20) \u5171 20 \u5b57\u8282", "usage": 200, "regions": [[20, 100], [120, 100]]}, {"timestamp": 12600, "type": "reload", "desc": "\u52a0\u8f7d\u865a\u62df\u5730\u5740 [240, 360) \u5171 120 \u5b57\u8282", "usage": 320, "regions": [[20, 100], [120, 100], [240, 120]]}, {"timestamp": 17400, "type": "visit", "desc": "\u8bbf\u95ee\u8bf7\u6c42 R2", "usage": 320, "regions": [[20, 100], [120, 100], [240, 120]]}, {"timestamp": 19400, "type": "offload", "desc": "\u5378\u8f7d\u865a\u62df\u5730\u5740 [240, 260) \u5171 20 \u5b57\u8282", "usage": 300, "regions": [[20, 100], [120, 100], [260, 100]]}, {"timestamp": 20200, "type": "reload", "desc": "\u52a0\u8f7d\u865a\u62df\u5730\u5740 [220, 240) \u5171 20 \u5b57\u8282", "usage": 320, "regions": [[20, 100], [120, 100], [220, 20], [260, 100]]}, {"timestamp": 21000, "type": "visit", "desc": "\u8bbf\u95ee\u8bf7\u6c42 R3", "usage": 320, "regions": [[20, 100], [120, 100], [220, 20], [260, 100]]}, {"timestamp": 25000, "type": "offload", "desc": "\u5378\u8f7d\u865a\u62df\u5730\u5740 [20, 110) \u5171 90 \u5b57\u8282", "usage": 230, "regions": [[110, 10], [120, 100], [220, 20], [260, 100]]}, {"timestamp": 28600, "type": "reload", "desc": "\u52a0\u8f7d\u865a\u62df\u5730\u5740 [360, 450) \u5171 90 \u5b57\u8282", "usage": 320, "regions": [[110, 10], [120, 100], [220, 20], [260, 100], [360, 90]]}, {"timestamp": 32200, "type": "visit", "desc": "\u8bbf\u95ee\u8bf7\u6c42 R4", "usage": 320, "regions": [[110, 10], [120, 100], [220, 20], [260, 100], [360, 90]]}, {"timestamp": 34200, "type": "offload", "desc": "\u5378\u8f7d\u865a\u62df\u5730\u5740 [260, 280) \u5171 20 \u5b57\u8282", "usage": 300, "regions": [[110, 10], [120, 100], [220, 20], [280, 80], [360, 90]]}, {"timestamp": 35000, "type": "reload", "desc": "\u52a0\u8f7d\u865a\u62df\u5730\u5740 [100, 110) \u5171 10 \u5b57\u8282", "usage": 310, "regions": [[100, 10], [110, 10], [120, 100], [220, 20], [280, 80], [360, 90]]}, {"timestamp": 35400, "type": "reload", "desc": "\u52a0\u8f7d\u865a\u62df\u5730\u5740 [240, 250) \u5171 10 \u5b57\u8282", "usage": 320, "regions": [[100, 10], [110, 10], [120, 100], [220, 20], [240, 10], [280, 80], [360, 90]]}, {"timestamp": 35800, "type": "visit", "desc": "\u8bbf\u95ee\u8bf7\u6c42 R5", "usage": 320, "regions": [[100, 10], [110, 10], [120, 100], [220, 20], [240, 10], [280, 80], [360, 90]]}, {"timestamp": 37800, "type": "offload", "desc": "\u5378\u8f7d\u865a\u62df\u5730\u5740 [100, 250) \u5171 150 \u5b57\u8282", "usage": 170, "regions": [[280, 80], [360, 90]]}, {"timestamp": 43800, "type": "offload", "desc": "\u5378\u8f7d\u865a\u62df\u5730\u5740 [280, 330) \u5171 50 \u5b57\u8282", "usage": 120, "regions": [[330, 30], [360, 90]]}, {"timestamp": 45800, "type": "reload", "desc": "\u52a0\u8f7d\u865a\u62df\u5730\u5740 [500, 700) \u5171 200 \u5b57\u8282", "usage": 320, "regions": [[330, 30], [360, 90], [500, 200]]}, {"timestamp": 53800, "type": "visit", "desc": "\u8bbf\u95ee\u8bf7\u6c42 R6", "usage": 320, "regions": [[330, 30], [360, 90], [500, 200]]}, {"timestamp": 55800, "type": "fin", "desc": "\u6240\u6709\u8bf7\u6c42\u5b8c\u6210", "usage": 320, "regions": [[330, 30], [360, 90], [500, 200]]}];
        const maxVaddr = 700;
        const hbmCapacity = 320;
        
        let currentStep = 0;
        let isPlaying = false;
        let playInterval = null;
        
        function updateDisplay() {
            if (currentStep < 0 || currentStep >= states.length) return;
            
            const state = states[currentStep];
            
            // 更新步骤信息
            document.getElementById('stepInfo').textContent = 
                `步骤 ${currentStep + 1}/${states.length} | 时间: ${state.timestamp}`;
            
            // 更新事件横幅
            const banner = document.getElementById('eventBanner');
            banner.textContent = state.desc;
            banner.className = 'event-banner event-' + state.type;
            
            // 更新使用量
            const usage = state.usage;
            const percentage = (usage / hbmCapacity * 100).toFixed(1);
            document.getElementById('usageText').textContent = 
                `${usage} / ${hbmCapacity} (${percentage}%)`;
            
            const progressBar = document.getElementById('progressBar');
            progressBar.style.width = percentage + '%';
            progressBar.textContent = percentage + '%';
            
            // 更新内存条
            const memoryBar = document.getElementById('memoryBar');
            memoryBar.innerHTML = '';
            
            state.regions.forEach(([addr, size]) => {
                const segment = document.createElement('div');
                segment.className = 'memory-segment';
                const left = (addr / maxVaddr * 100).toFixed(2);
                const width = (size / maxVaddr * 100).toFixed(2);
                segment.style.left = left + '%';
                segment.style.width = width + '%';
                segment.title = `[${addr}, ${addr + size}) = ${size} 字节`;
                
                segment.addEventListener('mouseenter', (e) => {
                    const tooltip = document.getElementById('tooltip');
                    tooltip.textContent = segment.title;
                    tooltip.style.display = 'block';
                });
                
                segment.addEventListener('mousemove', (e) => {
                    const tooltip = document.getElementById('tooltip');
                    tooltip.style.left = e.pageX + 10 + 'px';
                    tooltip.style.top = e.pageY + 10 + 'px';
                });
                
                segment.addEventListener('mouseleave', () => {
                    document.getElementById('tooltip').style.display = 'none';
                });
                
                memoryBar.appendChild(segment);
            });
            
            // 更新区域列表
            const regionsList = document.getElementById('regionsList');
            if (state.regions.length === 0) {
                regionsList.innerHTML = '<div style="color: #888; text-align: center; padding: 20px;">(空)</div>';
            } else {
                regionsList.innerHTML = state.regions.map(([addr, size]) => 
                    `<div class="region-item">[${addr.toString().padStart(4)},${(addr + size).toString().padStart(4)}) = ${size.toString().padStart(3)} 字节</div>`
                ).join('');
            }
        }
        
        function firstStep() {
            currentStep = 0;
            updateDisplay();
        }
        
        function lastStep() {
            currentStep = states.length - 1;
            updateDisplay();
        }
        
        function prevStep() {
            if (currentStep > 0) {
                currentStep--;
                updateDisplay();
            }
        }
        
        function nextStep() {
            if (currentStep < states.length - 1) {
                currentStep++;
                updateDisplay();
            } else {
                pause();
            }
        }
        
        function play() {
            isPlaying = true;
            document.getElementById('playBtn').textContent = '⏸ 暂停';
            const speed = parseInt(document.getElementById('speedSlider').value);
            playInterval = setInterval(() => {
                nextStep();
            }, speed);
        }
        
        function pause() {
            isPlaying = false;
            document.getElementById('playBtn').textContent = '▶ 播放';
            if (playInterval) {
                clearInterval(playInterval);
                playInterval = null;
            }
        }
        
        function playPause() {
            if (isPlaying) {
                pause();
            } else {
                play();
            }
        }
        
        // 速度滑块
        document.getElementById('speedSlider').addEventListener('input', (e) => {
            const speed = parseInt(e.target.value);
            const speedFactor = (2100 - speed) / 1000;
            document.getElementById('speedLabel').textContent = speedFactor.toFixed(1) + 'x';
            
            if (isPlaying) {
                pause();
                play();
            }
        });
        
        // 键盘快捷键
        document.addEventListener('keydown', (e) => {
            if (e.key === 'ArrowLeft') prevStep();
            else if (e.key === 'ArrowRight') nextStep();
            else if (e.key === ' ') {
                e.preventDefault();
                playPause();
            }
            else if (e.key === 'Home') firstStep();
            else if (e.key === 'End') lastStep();
        });
        
        // 初始化
        updateDisplay();
    </script>
</body>
</html>
